// Tree.m -- a tree object, subclass and formed of Branches

#import <appkit/appkit.h>
#import "ForestCamera.h"
#import "Tree.h"
extern long random();
id globalShader;

@implementation Tree
- init
{	[super init];
	random_mode = 1;
	[self inzVars :0];
	return self;
}

 // access generation parmaeters
- (double) angle						{return angle;}
- (double) angle :(double)angleArg		{return angle = angleArg;}
- (double) twist						{return twist;}
- (double) twist :(double)twistArg		{return twist = twistArg;}
- (double) shrink						{return shrink;}
- (double) shrink :(double)shrinkArg	{return shrink = shrinkArg;}
- (int) branchFactor					{return branchFactor;}
- (int) branchFactor :(int)branchFactorArg
										{return branchFactor=branchFactorArg;}
- (int) levels							{return levels;}
- (int) levels :(int)levelsArg			{return levels = levelsArg;}

- (double) SDangle						{return SDangle;}
- (double) SDangle :(double)SDangleArg	{return SDangle = SDangleArg;}
- (double) SDtwist						{return SDtwist;}
- (double) SDtwist :(double)SDtwistArg	{return SDtwist = SDtwistArg;}
- (double) SDshrink						{return SDshrink;}
- (double) SDshrink :(double)SDshrinkArg{return SDshrink = SDshrinkArg;}
- (double) SDbranchFactor				{return SDbranchFactor;}
- (double) SDbranchFactor :(double)SDbranchFactorArg {return 
										SDbranchFactor = SDbranchFactorArg;}
- (double) SDlevels						{return SDlevels;}
- (double) SDlevels :(double)SDlevelsArg{return SDlevels = SDlevelsArg;}


- plantIn :(ForestCamera *)forestArg
{	RtBound box;
	RtPoint eyePt, viewPt;
	float rollAng, dist, d, size, x,y,z;
	int i;
	Branch *trunk = [[Branch alloc] init];
	id oldWorldShape = [forestArg setWorldShape :self];

	srandom(lastSeed = random_mode? time(0): lastSeed);
	
	if (oldWorldShape == self)
		oldWorldShape = descendant;
	[oldWorldShape freeAll]; // free any old (parts of) trees
	forest = forestArg;
	
	[trunk setShader :globalShader];
	[trunk setBoundingBox :0 :0 :0 :0 :0 :1];
	//[[self descendant] free];
	[self linkDescendant :trunk];
	[trunk tree :self];
	[trunk addBranches :1 inTree :self];
	[self getBoundingBox :&box];

	 // Position viewpoint at center of box:
	[forest getEyeAt :&eyePt toward :&viewPt roll :&rollAng];
	viewPt[0] = x = (box[0]+box[1])*0.5;
	viewPt[1] = y = (box[2]+box[3])*0.5;
	viewPt[2] = z = (box[4]+box[5])*0.5;
	[forest setEyeAt :eyePt toward :viewPt roll :-90.0];

	 // Position the camera at a distance so the tree fills screen:
	for (i=0, size=0.01; i<6; i+=2)	/* compute the largest dimension of the */
	{	d = box[i] - box[i+1];		/*   surrounding box.*/
		if (d < 0)					
			d = -d;
		if (d > size)
			size = d;
	}
	dist = sqrt((x-=eyePt[0])*x + (y-=eyePt[1])*y + (z-=eyePt[2])*z);
	[forest moveEyeBy :0.0 :0.0 :2.2*size - dist];

	return self;
}


typedef struct
{	int levels, branchFactor;
	float angle, twist, shrink;
	int SDbranchFactor, SDlevels;
	float SDangle, SDtwist,SDshrink;
} treeType;
#define nTrees 6
treeType trees[nTrees] = {
	{4,  2, 45.0, 90.0, 0.70},				// inz
	{5,  2, 20.0, 73.0, 0.83},				// elm
	{3,  5, 35.0, 73.0, 0.75},				// queen annes lace
	{7,  2, 23.0,  0.0, 0.80},				// fan
	{7,  2, 90.0, 90.0, 0.67},				// tv antenna
	{20, 1, 20.7,  0.7, 0.85},				// hook
};

- specis :sender
{	[self inzVars :[sender selectedTag]];
	[self plantIn :forest];
	[forest newParameter :nil];
	[forest redrawNow :nil];
	return self;
}

- (int) inzVars :(int) index
{	treeType *nt = &trees[index];
	if (index < 0 || index >= nTrees)
		return 0;
	angle			= nt->angle;
	twist			= nt->twist;
	shrink			= nt->shrink;
	branchFactor	= nt->branchFactor;
	levels			= nt->levels;
	SDangle			= nt->SDangle;
	SDtwist			= nt->SDtwist;
	SDshrink		= nt->SDshrink;
	SDbranchFactor	= nt->SDbranchFactor;
	SDlevels		= nt->SDlevels;
	return 1;
}

- randomMode :sender;
{	int lastRandom_mode = random_mode;
	random_mode = [sender selectedTag];
	if ((lastRandom_mode && random_mode) || (!lastRandom_mode && !random_mode))
	{	if ([forest redraw_mode])
		{	[self plantIn :forest];
			[forest redrawNow :nil];
	}	}
	return self;
}

- (float) randVal :(treeParameter) param;
{	float r = ((random()&0xfffffff)/(float)0x8000000 - 1.0), val=0, sd=0;

	switch (param)
	{	case ANGLE:			return (angle + r * SDangle);
		case TWIST:			return (twist + r * SDtwist);
		case SHRINK:		return (shrink + r * SDshrink);
		case BRANCHFACTOR:	return (branchFactor + r * SDbranchFactor);
		case LEVELS:		return (levels + r * SDlevels);
	}
	return 0.0;
}

@end